﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace System
{
	public static class TypeExtensions
	{
		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface "IQueryable"
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsQueryable( this Type t )
		{
			return ( t != null
				&& typeof( IQueryable ).IsAssignableFrom( t ) )
				|| t == typeof( Queryable );
		}

		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface <see cref="System.Collections.Generic.ICollection"/>
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsCollection( this Type t )
		{
			Type itemType;
			return t.IsCollection( out itemType );
		}
		
		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface <see cref="System.Collections.Generic.ICollection"/>
		/// e retorna o tipo do item da coleção
		/// </summary>
		/// <param name="t"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public static bool IsCollection( this Type t , out Type itemType)
		{
			if ( t != null
				&& t.IsGenericType
				&& t.GetGenericTypeDefinition().Equals( typeof( ICollection<> ) ) )
			{
				itemType = t.GetGenericArguments()[ 0 ];
				return true;
			}
			else
			{
				itemType = null;
				return false;
			}
		}

		/// <summary>
		/// Indica se o tipo <paramref name="t"/> implementa a interface "IEnumerable"
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool IsEnumerable( this Type t )
		{
			return t != null && typeof( global::System.Collections.IEnumerable ).IsAssignableFrom( t );
		}

		/// <summary>
		/// Indica se <paramref name="T"/> implementa a interface <typeparamref name="T"/>
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="t"></param>
		/// <returns></returns>
		public static bool Implements<T>( this Type t )
		{
			return t != null && typeof( T ).IsAssignableFrom( t );
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="t"></param>
		/// <param name="methodName"></param>
		/// <param name="parameters"></param>
		public static MethodCaller Call( this Type t, string methodName, params object[] parameters )
		{
			return new MethodCaller( t.GetMethods().First( method => method.Name == methodName ), parameters );
		}
	}

	public class MethodCaller
	{

		internal MethodCaller( MethodInfo method, object[] parameters )
		{
			this.Method = method;
			this.Parameters = parameters;
		}

		public MethodInfo Method
		{ get; private set; }

		public object[] Parameters
		{ get; private set; }


		public object Of( object instance )
		{
			return this.Method.Invoke( instance, this.Parameters );
		}
	}
}
